1 module hip.windowing.platforms.x11; 2 3 version(Android){} 4 else version(linux) version = X11; 5 version(X11): 6 7 import core.stdc.stdio; 8 9 import hip.windowing.platforms.x11lib.glx; 10 import hip.windowing.platforms.x11lib.x11; 11 import hip.windowing.events; 12 import hip.windowing.input; 13 14 public import hip.windowing.platforms.x11lib.x11; 15 16 package struct X11WindowData 17 { 18 Display* display; 19 Window window; 20 Screen* screen; 21 XVisualInfo* visual; 22 int screenId; 23 int width, height; 24 Colormap colormap; 25 GLXContext glContext; 26 Atom atomWmDeleteWindow; 27 28 } 29 package X11WindowData x11win; 30 31 package __gshared bool ctxErrorOccurred = false; 32 33 package extern(C) nothrow @system @nogc 34 int ctxErrorHandler( Display *dpy, XErrorEvent *ev ) 35 { 36 ctxErrorOccurred = true; 37 return 0; 38 } 39 40 nothrow @nogc bool initializeOpenGL(int majorVersion, int minorVersion) 41 { 42 GLint[23] glxAttribs = [ 43 GLX_X_RENDERABLE , True, 44 GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT, 45 GLX_RENDER_TYPE , GLX_RGBA_BIT, 46 GLX_X_VISUAL_TYPE , GLX_TRUE_COLOR, 47 GLX_RED_SIZE , 8, 48 GLX_GREEN_SIZE , 8, 49 GLX_BLUE_SIZE , 8, 50 GLX_ALPHA_SIZE , 8, 51 GLX_DEPTH_SIZE , 24, 52 GLX_STENCIL_SIZE , 8, 53 GLX_DOUBLEBUFFER , True, 54 None 55 ]; 56 57 int fbcount; 58 GLXFBConfig* fbc = glXChooseFBConfig(x11win.display, x11win.screenId, glxAttribs.ptr, &fbcount); 59 if (fbc is null || fbcount == 0) 60 { 61 printf("Failed to retrieve framebuffer.\n"); 62 XCloseDisplay(x11win.display); 63 return 1; 64 } 65 66 // Pick the FB config/visual with the most samples per pixel 67 int best_fbc = -1, worst_fbc = -1, best_num_samp = -1, worst_num_samp = 999; 68 for (int i = 0; i < fbcount; ++i) { 69 XVisualInfo *vi = glXGetVisualFromFBConfig( x11win.display, fbc[i] ); 70 if ( vi != null) { 71 int samp_buf, samples; 72 glXGetFBConfigAttrib( x11win.display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf ); 73 glXGetFBConfigAttrib( x11win.display, fbc[i], GLX_SAMPLES , &samples ); 74 75 if ( best_fbc < 0 || (samp_buf && samples > best_num_samp) ) { 76 best_fbc = i; 77 best_num_samp = samples; 78 } 79 if ( worst_fbc < 0 || !samp_buf || samples < worst_num_samp ) 80 worst_fbc = i; 81 worst_num_samp = samples; 82 } 83 XFree( vi ); 84 } 85 86 87 //Some would try to get the best framebuffer, but, is that really necessary? Get the first 88 GLXFBConfig bestFbc = fbc[best_fbc]; 89 XFree(fbc); 90 x11win.visual = glXGetVisualFromFBConfig(x11win.display, bestFbc); 91 if(x11win.visual == null) 92 { 93 printf("Could not create correct visual window \n"); 94 XCloseDisplay(x11win.display); 95 return false; 96 } 97 98 //Open the window 99 XSetWindowAttributes windowAttribs; 100 windowAttribs.border_pixel = BlackPixel(x11win.display, x11win.screenId); 101 windowAttribs.background_pixel = WhitePixel(x11win.display, x11win.screenId); 102 windowAttribs.override_redirect = True; 103 windowAttribs.colormap = XCreateColormap( 104 x11win.display, RootWindow(x11win.display, x11win.screenId), x11win.visual.visual, AllocNone 105 ); 106 107 windowAttribs.event_mask = PointerMotionMask | 108 ButtonPressMask | 109 ButtonReleaseMask | 110 KeyPressMask | 111 KeyReleaseMask | 112 EnterWindowMask | 113 LeaveWindowMask | 114 ExposureMask; 115 116 117 118 x11win.window = XCreateWindow( 119 x11win.display, 120 RootWindow(x11win.display, x11win.screenId), 121 0, 0, x11win.width, x11win.height, 122 0, x11win.visual.depth, InputOutput, x11win.visual.visual, 123 CWBackPixel | CWColormap | CWBorderPixel | CWEventMask, 124 &windowAttribs 125 ); 126 if(!x11win.window) 127 { 128 printf("Could not create XWindow\n"); 129 return false; 130 } 131 132 Atom atomWmDeleteWindow = XInternAtom(x11win.display, "WM_DELETE_WINDOW", False); 133 if(atomWmDeleteWindow == BadAlloc) 134 { 135 printf("X Server failed to allocate WM_DELETE_WINDOW\n"); 136 return false; 137 } 138 else if(atomWmDeleteWindow == BadValue) 139 { 140 printf("WM_DELETE_WINDOW is not a valid argument\n"); 141 return false; 142 } 143 x11win.atomWmDeleteWindow = atomWmDeleteWindow; 144 Status st = XSetWMProtocols(x11win.display, x11win.window, &atomWmDeleteWindow, 1); 145 if(st == BadAlloc) 146 { 147 printf("XServer failed to allocate resources for SetWMProtocols\n"); 148 return false; 149 } 150 else if(st == BadWindow) 151 { 152 printf("The window argument does not name a defined window\n"); 153 return false; 154 } 155 156 const(char)* glExts = glXQueryExtensionsString(x11win.display, DefaultScreen(x11win.display)); 157 158 glXCreateContextAttribsARBProc glXCreateContextAttribsARB; 159 glXCreateContextAttribsARB = cast(glXCreateContextAttribsARBProc)glXGetProcAddressARB(cast(const(GLubyte)*)"glXCreateContextAttribsARB"); 160 161 GLXContext glContext; 162 163 164 if(!isExtensionSupported(glExts, "GLX_ARB_create_context") || glXCreateContextAttribsARB is null) 165 { 166 printf("glXCreateContextAttribsARB() not found, using old style GLX context"); 167 x11win.glContext = glContext = glXCreateNewContext(x11win.display, bestFbc, GLX_RGBA_TYPE, null, True); 168 } 169 else 170 { 171 int[7] context_attribs = 172 [ 173 GLX_CONTEXT_MAJOR_VERSION_ARB, 3, 174 GLX_CONTEXT_MINOR_VERSION_ARB, 3, //3.3 is the minimum here 175 GLX_CONTEXT_FLAGS_ARB , GLX_CONTEXT_CORE_PROFILE_BIT_ARB, 176 None 177 ]; 178 179 x11win.glContext = glContext = glXCreateContextAttribsARB(x11win.display, bestFbc, null, 180 True, context_attribs.ptr ); 181 } 182 if(x11win.glContext == null) 183 { 184 printf("Could not create GLX Context\n"); 185 return false; 186 } 187 XSync(x11win.display, False); 188 189 if (!glXIsDirect (x11win.display, x11win.glContext)) 190 printf("Indirect GLX rendering context obtained\n"); 191 if(glXMakeCurrent(x11win.display, x11win.window, x11win.glContext) == 0) 192 { 193 printf("Could not make GLX Context as current\n"); 194 return false; 195 } 196 //Show Window 197 XClearWindow(x11win.display, x11win.window); 198 XMapRaised(x11win.display, x11win.window); 199 XStoreName(x11win.display, x11win.window, "HipremeEngine"); 200 201 setVsyncActive(false); 202 203 204 return true; 205 } 206 207 void show(){} 208 void swapBuffer() 209 { 210 glXSwapBuffers(x11win.display, x11win.window); 211 } 212 213 void setVsyncActive(bool active) @nogc nothrow @system 214 { 215 static bool loadedSymbols = false; 216 if(!loadedSymbols) 217 { 218 glXSwapIntervalEXT = cast(glXSwapIntervalEXTProc)glXGetProcAddressARB(cast(GLubyte*)"glXSwapIntervalEXT"); 219 glXSwapIntervalMESA = cast(glXSwapIntervalMESAProc)glXGetProcAddressARB(cast(GLubyte*)"glXSwapIntervalMESA"); 220 glXSwapIntervalSGI = cast(glXSwapIntervalSGIProc)glXGetProcAddressARB(cast(GLubyte*)"glXSwapIntervalSGI"); 221 loadedSymbols = true; 222 } 223 glXSwapIntervalEXT(x11win.display, x11win.window, cast(int)active); 224 glXSwapIntervalMESA(cast(int)active); 225 glXSwapIntervalSGI(cast(int)active); 226 } 227 228 void setWindowName(string name) 229 { 230 XStoreName(x11win.display, x11win.window, name.ptr); 231 } 232 233 234 pragma(inline) wchar convertKeycodeToScancode(XKeyEvent* ev) 235 { 236 char[2] buffer = '\0'; 237 KeySym ks; 238 int allocated = XLookupString(ev, buffer.ptr, buffer.length, &ks, null); 239 if(allocated > 1) 240 printf("%*s", cast(int)buffer.length, buffer.ptr); 241 return *cast(wchar*)(cast(void*)buffer.ptr); 242 } 243 244 void poll() 245 { 246 XEvent ev; 247 while (XPending(x11win.display) > 0) 248 { 249 XNextEvent(x11win.display, &ev); 250 switch(ev.type) 251 { 252 case Expose: 253 { 254 XWindowAttributes attribs; 255 XGetWindowAttributes(x11win.display, x11win.window, &attribs); 256 printf("Expose event\n"); 257 break; 258 } 259 case ClientMessage: 260 if(ev.xclient.data.l[0] == x11win.atomWmDeleteWindow && onWindowClosed != null) 261 onWindowClosed(); 262 break; 263 case DestroyNotify: 264 { 265 if(onWindowClosed != null) 266 onWindowClosed(); 267 break; 268 } 269 case ButtonPress: 270 { 271 int x = ev.xbutton.x; 272 int y = ev.xbutton.y; 273 switch(ev.xbutton.button) 274 { 275 case 1: //Left 276 if(onMouseDown != null) 277 onMouseDown(HipWindowingMouseButton.left, x, y); 278 break; 279 case 2: //Middle 280 if(onMouseDown != null) 281 onMouseDown(HipWindowingMouseButton.middle, x, y); 282 break; 283 case 3: //Right 284 if(onMouseDown != null) 285 onMouseDown(HipWindowingMouseButton.right, x, y); 286 break; 287 case 4: //Scroll up 288 if(onMouseWheel != null) 289 onMouseWheel(0, -1); 290 break; 291 case 5: //Scroll down 292 if(onMouseWheel != null) 293 onMouseWheel(0, 1); 294 break; 295 default: break; 296 } 297 } break; 298 case ButtonRelease: 299 { 300 int x = ev.xbutton.x; 301 int y = ev.xbutton.y; 302 switch(ev.xbutton.button) 303 { 304 case 1: //Left 305 if(onMouseUp != null) 306 onMouseUp(HipWindowingMouseButton.left, x, y); 307 break; 308 case 2: //Middle 309 if(onMouseUp != null) 310 onMouseUp(HipWindowingMouseButton.middle, x, y); 311 break; 312 case 3: //Right 313 if(onMouseUp != null) 314 onMouseUp(HipWindowingMouseButton.right, x, y); 315 break; 316 default: break; 317 } 318 } break; 319 case MotionNotify: 320 if(onMouseMove != null) 321 onMouseMove(ev.xmotion.x, ev.xmotion.y); 322 break; 323 case KeyPress: 324 if(onKeyDown != null) 325 onKeyDown(cast(uint)XKeycodeToKeysym(x11win.display, ev.xkey.keycode, ev.xkey.state & ShiftMask ? 1 : 0)); 326 // onKeyDown(convertKeycodeToScancode(&ev.xkey)); 327 break; 328 case KeyRelease: 329 if(onKeyUp != null) 330 onKeyUp(cast(uint)XKeycodeToKeysym(x11win.display, ev.xkey.keycode, ev.xkey.state & ShiftMask ? 1 : 0)); 331 // onKeyUp(convertKeycodeToScancode(&ev.xkey)); 332 break; 333 default:break; 334 } 335 } 336 } 337 338 ///Returns [width, height] 339 int[2] getWindowSize() 340 { 341 XWindowAttributes att; 342 XGetWindowAttributes(x11win.display, x11win.window, &att); 343 return [att.width, att.height]; 344 } 345 346 void setWindowSize(int width, int height) 347 { 348 uint change_values = CWWidth | CWHeight; 349 XWindowChanges values; 350 values.width = width; 351 values.height = height; 352 XConfigureWindow(x11win.display, x11win.window, change_values, &values); 353 } 354 355 bool destroy_GL_Context() 356 { 357 XDestroyWindow(x11win.display, x11win.window); 358 XCloseDisplay(x11win.display); 359 XFree(x11win.visual); 360 XFreeColormap(x11win.display, x11win.colormap); 361 glXDestroyContext(x11win.display, x11win.glContext); 362 return true; 363 } 364 365 int openWindow(int width, int height) 366 { 367 x11win.width = width; 368 x11win.height = height; 369 //Open the display 370 x11win.display = XOpenDisplay(null); 371 if(x11win.display == null) 372 { 373 printf("Could not open display.\n"); 374 return 1; 375 } 376 int glx_major, glx_minor; 377 if ( !glXQueryVersion(x11win.display, &glx_major, &glx_minor ) || 378 ( ( glx_major == 1 ) && ( glx_minor < 3 ) ) || ( glx_major < 1 ) ) 379 { 380 printf("Invalid GLX version\n"); 381 return 1; 382 } 383 x11win.screenId = DefaultScreen(x11win.display); 384 385 return 0; 386 }